package model;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;

@SuppressWarnings("unchecked")
public class SchrittList {
	private int boxLength;
	private int width;
	private int height;
	private ArrayList<Schritt>[] schritte;

	public SchrittList(int boxLength, int width, int height) {
		this.boxLength = boxLength;
		this.width = width / boxLength + 1;
		this.height = height / this.boxLength + 1;
		this.schritte = new ArrayList[this.width * this.height];
	}

	public Iterable<Schritt> get() {
		return new RecursiveIterable(schritte);
	}

	public Iterable<Schritt> getSchritteInRect(Rectangle r) {
		if (r.getHeight() > 2 * boxLength || r.getWidth() > 2 * boxLength) {
			return get();
		} else {
			return getSchritteUm((int) (r.getCenterX() / boxLength), (int) (r.getCenterY() / boxLength));
		}
	}
	
	public Iterable<Schritt> getSchritteUm(Schritt s) {
		return getSchritteUm(this.getX(s), this.getY(s));
	}

	public Iterable<Schritt> getSchritteUm(int x, int y) {
		// Auf Feldgröße anpassen
		x = Math.min(width - 2, Math.max(x, 1));
		y = Math.min(height - 2, Math.max(y, 1));

		return new RecursiveIterable(new Iterable[] {
				schritte[x - 1 + width * (y - 1)],
				schritte[x + width * (y - 1)],
				schritte[x + 1 + width * (y - 1)],
				schritte[x - 1 + width * (y)], schritte[x + width * (y)],
				schritte[x + 1 + width * (y)],
				schritte[x - 1 + width * (y + 1)],
				schritte[x + width * (y + 1)],
				schritte[x + 1 + width * (y + 1)], });
	}

	public void add(Schritt s) {
		int x = this.getX(s);
		int y = this.getY(s);
		if (this.schritte[x + y * width] == null)
			this.schritte[x + y * width] = new ArrayList<Schritt>();
		this.schritte[x + y * width].add(s);
	}

	private int getX(Schritt s) {
		return (int) (s.getMiddleX() / boxLength);
	}

	private int getY(Schritt s) {
		return (int) (s.getMiddleY() / boxLength);
	}

	class RecursiveIterable implements Iterable<Schritt> {
		private Iterable<Schritt>[] objects;

		public RecursiveIterable(Iterable<Schritt>[] objects) {
			this.objects = objects;
		}

		@Override
		public Iterator<Schritt> iterator() {
			return new Iterator<Schritt>() {
				private int i;
				private Iterator<Schritt>[] iterators;

				{
					iterators = new Iterator[objects.length];
					for (int i = 0; i < objects.length; i++) {
						if (objects[i] != null)
							iterators[i] = objects[i].iterator();
					}
				}

				@Override
				public boolean hasNext() {
					while (true) {
						if (i == objects.length)
							return false;

						if (iterators[i] != null && iterators[i].hasNext())
							return true;
						else
							i++;
					}
				}

				@Override
				public Schritt next() {
					if (!hasNext())
						throw new NoSuchElementException();
					return iterators[i].next();
				}

				@Override
				public void remove() {
					throw new UnsupportedOperationException();
				}
			};
		}
	}
	
	public interface Filter<T> {
		boolean apply(T type);
	}

	public void remove(Filter<Schritt> filter) {
		for (int i = 0; i < schritte.length; i++) {
			ArrayList<Schritt> liste = schritte[i];
			if (liste != null) {
				for (int j = liste.size() - 1; j >= 0; j--) {
					if (filter.apply(liste.get(j)))
						liste.remove(j);
				}
			}
		}
	}
}